﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Google.GData.Client;
using Google.GData.Photos;
using Google.GData.Extensions;
using System.IO;
using System.Net;

namespace PicasaWebDownloader
{
    class Program
    {
        static void Main(string[] args)
        {
            string commandLineArgs = string.Join(" ", args);
            string inpUsername = "";
            string inpPassword = "";
            string inpSavePath = "";
            bool inpOverwrite = true;
            int totalPics = 0;
            int nbrOfAlbums = 0;
            
            // Check if no arguments was put in, display help
            if (args.Length == 0 || string.IsNullOrEmpty(args[0]) || commandLineArgs.Contains("-help"))
            {
                Console.WriteLine("PicasaWebDownloader: A utility to download all your Picasa Web Albums.");
                Console.WriteLine("       Author: Kristofer Källsbo (http://www.hackviking.com)");
                Console.WriteLine();
                Console.WriteLine("Usage: picasawebdownloader.exe folderPath -u:USERNAME -p:PASSWORD [options]");
                Console.WriteLine();
                Console.WriteLine("Example Usage:");
                Console.WriteLine("   picasawebdownloader.exe \"C:\\Users\\Public\\Pictures\\My Pictures\\\" -u:USERNAME -p:PASSWORD");
                Console.WriteLine();
                Console.WriteLine("Options:");
                Console.WriteLine("   -u:USERNAME         Picasa Username");
                Console.WriteLine("   -p:PASSWORD         Picasa Password");
                Console.WriteLine("   -n                  Only download files that aren't allready on disk");
                Console.WriteLine();
                return;
            }

            // Parse command parameters
            inpSavePath = args[0] + @"\";
            if (!Directory.Exists(inpSavePath))
            {
                Console.WriteLine("Error: Download folder doesn't exist!");
                return;
            }

            if (commandLineArgs.Contains("-u:"))
            {
                int startIndex = commandLineArgs.IndexOf("-u:") + 3;
                inpUsername = commandLineArgs.Substring(startIndex, commandLineArgs.IndexOf(" ", startIndex + 1) - startIndex);
            }

            if (commandLineArgs.Contains("-p:"))
            {
                int startIndex = commandLineArgs.IndexOf("-p:") + 3;
                if(commandLineArgs.IndexOf(" ", startIndex + 1) == -1)
                    inpPassword = commandLineArgs.Substring(startIndex, commandLineArgs.Length - startIndex);
                else
                    inpPassword = commandLineArgs.Substring(startIndex, commandLineArgs.IndexOf(" ", startIndex + 1) - startIndex);
            }

            inpOverwrite = !commandLineArgs.Contains("-n");
            
            StreamWriter logg = File.CreateText(inpSavePath + @"logg.txt");
            DateTime start = DateTime.Now;

            logg.WriteLine("Picasa Web Downloader by Kristofer Källsbo version 1.0");
            logg.WriteLine("Started download at {0}", start.ToString());
            logg.WriteLine();
            logg.Flush();

            #region Connect to picasa
            // Connect to picasa and create service object
            Console.WriteLine("Connecting to Picasa...");
            logg.WriteLine("Connecting to Picasa...");
            PicasaService pService = new PicasaService("PicasaWebDownloader");
            pService.setUserCredentials(inpUsername, inpPassword);

            // Check that the credentials work
            try
            {
                pService.QueryClientLoginToken();
                Console.WriteLine("Logon succesfull!");
                logg.WriteLine("Logon succesfull!");
            }
            catch (Google.GData.Client.InvalidCredentialsException)
            {
                Console.WriteLine("Error: Invalid credentials!");
                logg.WriteLine("Error: Invalid credentials!");
                logg.Flush();
                logg.Close();
                return;
            }
            #endregion

            #region Get album list
            // Get the albumlist
            Console.WriteLine("Collecting album list...");
            logg.WriteLine("Collecting album list...");
            logg.WriteLine();
            logg.Flush();
            PicasaFeed albumFeed;
            try
            {
                // Get all albums
                AlbumQuery albumQuery = new AlbumQuery(PicasaQuery.CreatePicasaUri(inpUsername));
                albumFeed = pService.Query((KindQuery)albumQuery);
            }
            catch (Exception)
            {
                return;
            }

            #endregion

            #region Process album list
            if (albumFeed != null && albumFeed.Entries.Count > 0)
            {
                foreach (PicasaEntry albumEntry in (AtomCollectionBase<AtomEntry>)albumFeed.Entries)
                {
                    string nbrofphotos = albumEntry.GetPhotoExtensionValue("numphotos");
                    totalPics += int.Parse(nbrofphotos); // TODO: Failsafe on parse                    
                    nbrOfAlbums += 1;

                    // Create folder for album
                    Directory.CreateDirectory(inpSavePath + albumEntry.Title.Text);
                    Console.WriteLine(albumEntry.Title.Text + " (" + nbrofphotos + " )");
                    logg.WriteLine(albumEntry.Title.Text + " (" + nbrofphotos + " )");

                    #region Get media list from album
                    // List all the pictures
                    PicasaFeed mediaFeed;
                    string albumURI = PicasaQuery.CreatePicasaUri(inpUsername, albumEntry.GetPhotoExtensionValue("id")) + "?imgmax=d"; // Adding imgmax=d to feedurl to get original image

                    try
                    {
                        // Get album feed
                        mediaFeed = pService.Query(new PhotoQuery(albumURI));
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                    #endregion

                    #region Process media list
                    if (mediaFeed != null && mediaFeed.Entries.Count > 0)
                    {
                        foreach (PicasaEntry item in mediaFeed.Entries)
                        {
                            #region Get URL to download
                            string URLtoDownload = "";
                            string contentType = "";

                            // Check to see if this is a video
                            if (item.Media.Contents.Count > 1)
                            {
                                // Get video link with the best resulotion
                                foreach (var content in item.Media.Contents.Where(d => d.Type == "video/mpeg4").OrderBy(d => d.Height).ThenBy(d => d.Width).Take(1))
                                {
                                    URLtoDownload = content.Url;
                                    contentType = content.Type;
                                }
                            }
                            else if (item.Media.Contents.Count > 0) // Check to see if there is any content att all, fail safe
                            {
                                URLtoDownload = item.Media.Contents[0].Url;
                                contentType = item.Media.Contents[0].Type;
                            }
                            #endregion

                            #region File downloader
                            // Download file
                            if (URLtoDownload != "")
                            {
                                if (!inpOverwrite && File.Exists(inpSavePath + albumEntry.Title.Text + @"\" + item.Title.Text))
                                {
                                    // We should only download file if it doesn't allready exist
                                    Console.WriteLine(item.Title.Text + " - allready exists!");
                                    logg.WriteLine(item.Title.Text + " - allready exists!");
                                }
                                else
                                {
                                    // Download file
                                    WebClient Client = new WebClient();

                                    try
                                    {
                                        Client.DownloadFile(URLtoDownload, inpSavePath + albumEntry.Title.Text + @"\" + item.Title.Text);
                                        Console.WriteLine(item.Title.Text + " - downloaded to: " + inpSavePath + albumEntry.Title.Text + @"\" + item.Title.Text);
                                        logg.WriteLine(item.Title.Text + " - downloaded to: " + inpSavePath + albumEntry.Title.Text + @"\" + item.Title.Text);
                                    }
                                    catch (WebException ex)
                                    {
                                        
                                        // Check if file name is wrong
                                        // Check if title is okey as a filename
                                        string filename = item.Title.Text;
                                        if (filename.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
                                        {
                                            #region Correct filename
                                            // Remove invalid charachters
                                            foreach (char c in Path.GetInvalidFileNameChars())
                                            {
                                                filename = filename.Replace(c.ToString(), "");
                                            }
                                            
                                            filename = filename.ToLower();

                                            // Check to see if it has a file ending
                                            if (contentType.Contains("video") && !filename.Contains(".mov"))
                                                filename += ".mov";
                                            else if (contentType.Contains("jpeg") && !filename.Contains(".jpg"))
                                                filename += ".jpg";
                                            else if (contentType.Contains("gif") && !filename.Contains(".gif"))
                                                filename += ".gif";

                                            // Try download agian
                                            try
                                            {
                                                Client.DownloadFile(URLtoDownload, inpSavePath + albumEntry.Title.Text + @"\" + filename);
                                                Console.WriteLine("Name of {0} was changed to {1} - to become a valid filename!", item.Title.Text, filename);
                                                Console.WriteLine(item.Title.Text + " - downloaded to: " + inpSavePath + albumEntry.Title.Text + @"\" + item.Title.Text);
                                                logg.WriteLine("Name of {0} was changed to {1} - to become a valid filename!", item.Title.Text, filename);
                                                logg.WriteLine(item.Title.Text + " - downloaded to: " + inpSavePath + albumEntry.Title.Text + @"\" + item.Title.Text);
                                            }
                                            catch (Exception InnerTryEx)
                                            {
                                                Console.WriteLine("Error: Unable to download " + item.Title.Text + " - " + InnerTryEx.Message);
                                                logg.WriteLine("##########################################################################");
                                                logg.WriteLine("Error: Unable to download " + item.Title.Text + " - " + InnerTryEx.Message);
                                                logg.WriteLine("Error url: {0}", URLtoDownload);
                                                logg.WriteLine("Error filename: {0}", item.Title.Text);
                                                logg.WriteLine("##########################################################################");
                                                logg.Flush();
                                            }
                                            #endregion
                                        }
                                        else
                                        {
                                            Console.WriteLine("Error: Unable to download " + item.Title.Text + " - " + ex.Message);
                                            logg.WriteLine("##########################################################################");
                                            logg.WriteLine("Error: Unable to download " + item.Title.Text + " - " + ex.Message);
                                            logg.WriteLine("Error url: {0}", URLtoDownload);
                                            logg.WriteLine("Error filename: {0}", item.Title.Text);
                                            logg.WriteLine("##########################################################################");
                                            logg.Flush();
                                        }
                                    }
                                }
                            }
                            else
                            {
                                Console.WriteLine("Error: URL to download " + item.Title.Text + " was not found!");
                                logg.WriteLine("Error: URL to download " + item.Title.Text + " was not found!");
                            }
                            #endregion
                            logg.Flush();
                        }
                    }
                    #endregion
                    logg.WriteLine("End of album {0}", albumEntry.Title.Text);
                    logg.WriteLine("***********************************************************************");
                    logg.WriteLine();
                    logg.Flush();
                }
            }
            #endregion

            #region Write summery and close logg
            Console.WriteLine(totalPics.ToString() + " photos in " + nbrOfAlbums.ToString() + " albums!");
            logg.WriteLine(totalPics.ToString() + " photos in " + nbrOfAlbums.ToString() + " albums!");
            DateTime end = DateTime.Now;
            logg.WriteLine("Run ended at {0}", end.ToString());
            TimeSpan runtime = end.Subtract(start);
            logg.WriteLine("Run took {0} days {1} hours {2} minutes {3} seconds...", new object[] { runtime.Days.ToString(), runtime.Hours.ToString(), runtime.Minutes.ToString(), runtime.Seconds.ToString() });
            logg.Flush();
            logg.Close();
            #endregion
#if DEBUG
            Console.WriteLine("Press any key to close...");
            Console.ReadLine();
#endif
        }
    }
}
